package cc.shacocloud.mirage.utils.converter;

import cc.shacocloud.mirage.utils.collection.SelfSortList;
import cc.shacocloud.mirage.utils.comparator.OrderComparator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * {@link TypeConverterSupport} 的实现
 *
 * @author 思追(shaco)
 */
public class TypeConverterSupportImpl implements TypeConverterSupport {
    
    protected final Map<ConverterCacheKey, Conversion> explicitConverterCache = new HashMap<>(256);
    
    protected final Map<TypeDescriptor, Conversion> targetConverterCache = new HashMap<>(128);
    
    protected final List<ConversionSupport> conversionSupportCache = new SelfSortList<>(16, OrderComparator.INSTANCE::getOrder);
    
    public TypeConverterSupportImpl() {
        // 注册内置转换器
        defaultConversion();
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public <T> @Nullable T convertIfNecessary(@Nullable Object source,
                                              @Nullable Class<T> requiredType,
                                              @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
        if (Objects.isNull(source)) return null;
        if (Objects.isNull(requiredType) && Objects.isNull(typeDescriptor)) {
            throw new IllegalArgumentException("requiredType 或 typeDescriptor 必须有一个不为空！");
        }
        
        // 判断是否为对象本身
        Class<?> type = Objects.nonNull(typeDescriptor) ? typeDescriptor.getType() : requiredType;
        if (type.isInstance(source)) {
            return (T) source;
        }
        
        // 生成缓存key
        TypeDescriptor sourceType = TypeDescriptor.forObject(source);
        TypeDescriptor targetType = Objects.nonNull(typeDescriptor) ? typeDescriptor : TypeDescriptor.valueOf(requiredType);
        ConverterCacheKey cacheKey = ConverterCacheKey.of(sourceType, targetType);
        
        Conversion conversion = null;
        
        // 优先匹配精确的转换器
        if (explicitConverterCache.containsKey(cacheKey)) {
            conversion = explicitConverterCache.get(cacheKey);
        }
        // 匹配目标类型转换器
        else if (targetConverterCache.containsKey(targetType)) {
            conversion = targetConverterCache.get(targetType);
        }
        // 循环匹配类型转换器
        else {
            for (ConversionSupport conversionSupport : conversionSupportCache) {
                if (conversionSupport.support(sourceType, targetType)) {
                    conversion = conversionSupport;
                    break;
                }
            }
        }
        
        if (Objects.nonNull(conversion)) {
            try {
                return (T) conversion.convert(source, sourceType, targetType);
            } catch (Exception e) {
                if (e instanceof ConversionException) {
                    throw e;
                }
                throw new ConversionException(sourceType, targetType, source, e);
            }
        }
        
        throw new NoSuchConversionException(sourceType, targetType, source);
    }
    
    @Override
    public void registry(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType, @NotNull Conversion conversion) {
        ConverterCacheKey cacheKey = ConverterCacheKey.of(sourceType, targetType);
        explicitConverterCache.put(cacheKey, conversion);
    }
    
    @Override
    public void registry(@NotNull TypeDescriptor targetType, @NotNull Conversion conversion) {
        targetConverterCache.put(targetType, conversion);
    }
    
    @Override
    public void registry(@NotNull ConversionSupport conversionSupport) {
        conversionSupportCache.add(conversionSupport);
    }
    
    /**
     * 默认转换器
     */
    protected void defaultConversion() {
        ServiceLoader<ConversionSupport> serviceLoader = ServiceLoader.load(ConversionSupport.class,
                ConversionSupport.class.getClassLoader());
        serviceLoader.forEach(this::registry);
    }
    
}
